In [1]:
%load_ext tensorboard
In [2]:
import wget
import os
import pickle
import tarfile
import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
import plotly.express as px

Наборы данных CIFAR10 и CIFAR100¶

**CIFAR10** и **CIFAR100** - классические наборы данных для решения задач классификации обучения с учителем. Принято рассматривать оба набора данных как единое целое, так как их отличие лишь в количестве классов на каждый набор (10 и 100 классов соответственно). Наборы данных были составлены экспертами из департамента Computer Science университета Торонто Alex Krizhevsky, Vinod Nair, и Geoffrey Hinton.

Набор данных CIFAR10 содержит 60000 цветных(RGB) изображений размером 32х32 пикселя из 10 классов. Количество изображений по классам распределено равномерно. Каждый класс содержит по 6000 соответствующих изображений. Набор данных разделен на train и test подмножества в соотношении 50000 и 10000 изображений. Эти классы полностью взаимоисключают друг друга. Между легковыми и грузовыми автомобилями нет перекрытия. "Автомобиль" включает в себя седаны, внедорожники и тому подобное. "Грузовик" включает в себя только большие грузовики. Ни то, ни другое не включает в себя пикапы.

Пример из случайных 10 изображений каждого класса:¶

airplane
automobile
bird
cat
deer
dog
frog
horse
ship
truck

Набор данных CIFAR100¶

Набор данных CIFAR100 мало отличается от своего младшего брата(CIFAR10) идеологически. Он содержит 100 классов, которые в свою очередь относятся к 20 суперклассам, которые их объединяют. Всего набор данных CIFAR100 содержит 60000 изображений из которых 50000 - тренировочный набор, 10000 - тестовый. Изображения распределены равномерно, соответственно каждый класс имеет по 600 экземпляров различных изображений.

В наборе данных представленны следующие классы и суперклассы:¶

Superclass Classes
aquatic mammals beaver, dolphin, otter, seal, whale
fish aquarium fish, flatfish, ray, shark, trout
flowers orchids, poppies, roses, sunflowers, tulips
food containers bottles, bowls, cans, cups, plates
fruit and vegetables apples, mushrooms, oranges, pears, sweet peppers
household electrical devices clock, computer keyboard, lamp, telephone, television
household furniture bed, chair, couch, table, wardrobe
insects bee, beetle, butterfly, caterpillar, cockroach
large carnivores bear, leopard, lion, tiger, wolf
large man-made outdoor things bridge, castle, house, road, skyscraper
large natural outdoor scenes cloud, forest, mountain, plain, sea
large omnivores and herbivores camel, cattle, chimpanzee, elephant, kangaroo
medium-sized mammals fox, porcupine, possum, raccoon, skunk
non-insect invertebrates crab, lobster, snail, spider, worm
people baby, boy, girl, man, woman
reptiles crocodile, dinosaur, lizard, snake, turtle
small mammals hamster, mouse, rabbit, shrew, squirrel
trees maple, oak, palm, pine, willow
vehicles 1 bicycle, bus, motorcycle, pickup truck, train
vehicles 2 lawn-mower, rocket, streetcar, tank, tractor

Структура набора данных¶

Набор данных CIFAR100 и CIFAR10 предоставляются ввиде архива, который содержит 3 pickle файла в бинарном формате:

Имя файлаОписание
trainСодержит тренировочный набор данных в бинарном формате
testСодержит тестовый набор данных в бинарном формате
metaСодержит мета информацию о наборе данных. Например: список названий меток

Pickle файлы с тренировочными и тестовыми данными имеют одинаковую структуру. Отличие лишь в самих данных и их количестве.

Структура train и test наборов¶

При распаковке pickle файла в Python окружение мы получим словарь(dict). Словарь по сути является таблицей вида:

dict {
    str(filenames):     List[str], size == N
    str(batch_label):   str
    str(fine_labels):   List[int], size == N
    str(coarse_labels): List[int], size == N
    str(data):          np.ndarray((N, 3072))
}

Краткое описание каждого ключа словаря:¶

Ключ Тип Описание
filenames List of string Уникальные имена файлов с изображением для идентификации конкретного изображения и сохранения.
batch_label String Строковое описание к какому из пакетов принадлежит данный набор. В случае **CIFAR100** всегда является 'training batch 1 of 1' или 'testing batch 1 of 1'
fine_labels List of int Идентификатор класса принадлежащий к i-му изображению.
coarse_labels List of int Идентификатор суперкласса принадлежащий к i-му изображению.
coarse_labels Numpy ND Array Числовые массивы описывающие изображения в формате RGB. (См. далее)

Подробнее о ключе data:¶

По ключу data можно получить numpy.ndarray который содержит N "векторов". Каждый из векторов - отдельное изображение из набора данных.

>>> train_data, test_data, _ = load_CIFAR100()

>>> type(train_data)              --> dict
>>> train_data.keys()             --> dict_keys(['filenames', 'batch_label', 'fine_labels', 'coarse_labels', 'data'])

>>> data = train_data['data']
>>> data.shape                    --> (50000, 3072)
>>> data[0]                       --> array([255, 255, 255, ...,  10,  59,  79], dtype=uint8)

Каждый вектор можно представить в виде 32х32 RGB изображения. Первые 1024 элемента содержат в себе 32x32 значения для Red канала, следующие 1024 элемента - Blue, и последние - Green. Таким образом простым преобразованием numpy.reshape мы можем получить представление в виде 3x32x32:

>>> data = data.reshape(50000, 32, 32, 3)   -->   50000 изображение формата HxWxC.

Структура meta файла¶

При распаковке pickle файла в Python окружение мы получим словарь(dict). Словарь по сути является таблицей вида:

dict {
    str(fine_label_names):   List[str], size == 20
    str(coarse_label_names): List[str], size == 100
}

Краткое описание каждого ключа словаря:¶

Ключ Тип Описание
fine_label_names List of string Строковые названия меток классов. Например: fine_label_names[0] == 'apple'
coarse_label_names List of string Строковые названия меток суперклассов. Например: coarse_label_names[0] == 'aquatic_mammals'

Где используется набор данных¶

Традиционно эти два набора данных используются в качестве Benchmark для задачи классификации при крайне низком разрешении изображения. В качестве примеров статей можно привести этот список:

  • Efficient Adaptive Ensembling for Image Classification
  • Sharpness-Aware Minimization for Efficiently Improving Generalization
  • Big Transfer (BiT): General Visual Representation Learning
  • EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
  • GPipe: Efficient Training of Giant Neural Networks using Pipeline Parallelism

и во множестве других статей и работ.

Загрузка набора данных¶

Набор данных CIFAR100 предоставляется в 3х видах:

Вид данных Прямая ссылка Размер md5sum
CIFAR-100 python version Link 161 MB eb9058c3a382ffc7106e4002c42a8d85
CIFAR-100 matlab version Link 175 MB 6a4bfa1dcd5c9453dda6bb54194911f4
CIFAR-100 binary version(C) Link 161 MB 03b5dce01913d631647c71ecec9e9cb8

Загрузка данных из источника делится на 3 этапа:

  1. Скачивание архива
  2. Распаковка архива
  3. Загрузка данных в Python с помощью Pickle.
In [3]:
CIFAR100_URL="https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz"
In [4]:
def download_CIFAR100() -> str:
    """
    Загрузка архива с набором данных CIFAR-100.
    Return:
        str - путь к скачанному архиву.
    """
    print("Start downloading dataset: CIFAR 100.")
    print("Load from URL:", CIFAR100_URL)
    
    archive_path = wget.download(CIFAR100_URL)
    print()
    print("Downloaded to archive:", archive_path)
    return archive_path

def unpack_archive(archive_path: str) -> str:
    """
    Распаковка архива по указанному пути.
    Примечание: Архив должен иметь формат *.tar или *.tar.gz
    Inputs:
        archive_path [str] - Путь к архиву который нужно распаковать.
    Return:
        str - путь к директории куда был распакован архив
    """
    readmode = 'r:'
    if archive_path.endswith('.tar.gz'):
        readmode += "gz"
    
    tar = tarfile.open(archive_path, readmode)
    tar.extractall()
    tar.close()
    
    os.remove(archive_path)
    directory_with_data = os.path.abspath(archive_path).replace(".tar", "").replace(".gz", "")
    print("Unpacked to:", directory_with_data)
    
    return directory_with_data
    
    
def load_from_pickle_CIFAR100(directory_with_data: str):
    """
    Загрузить набор данных CIFAR-100 из pickle файлов.
    Inputs:
        directory_with_data [str] - Директория в которой расположены файлы набора данных. Директория обязательно должна содержать файлы train, test, meta.
    Return:
        Tuple[Dict, Dict, Dict] - Распакованные наборы Train, Test, Meta.
    """
    def unpickle(file):
        import pickle
        with open(file, 'rb') as fo:
            dict = pickle.load(fo, encoding='latin1')
        return dict
    
    train_data = unpickle(os.path.join(directory_with_data, "train"))
    test_data  = unpickle(os.path.join(directory_with_data, "test"))
    meta_data  = unpickle(os.path.join(directory_with_data, "meta"))
    
    return train_data, test_data, meta_data

Стадия загрузки и распаковки архива

In [5]:
archive = download_CIFAR100()
unpack_archive(archive)
Start downloading dataset: CIFAR 100.
Load from URL: https://www.cs.toronto.edu/~kriz/cifar-100-python.tar.gz
100% [..................................................] 169001437 / 169001437
Downloaded to archive: cifar-100-python.tar.gz
Unpacked to: /home/ilya/work/MAI/MAI_AI_labs/lab_8/cifar-100-python
Out[5]:
'/home/ilya/work/MAI/MAI_AI_labs/lab_8/cifar-100-python'

Стадия загрузки из Pickle

In [6]:
train_data, test_data, meta_data = load_from_pickle_CIFAR100("./cifar-100-python")

Визуализация примеров из набора данных¶

In [7]:
nrows = 3
ncols = 3
samples_ids = np.random.randint(0, len(train_data['data']), size=nrows * ncols).reshape(nrows, ncols)

fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(15, 16))

for i in range(nrows):
    for j in range(ncols):
        
        sample_id = samples_ids[i, j]
        sample = train_data['data'][sample_id].reshape((3, 32, 32)).transpose(1, 2, 0)
        ax[i][j].imshow(sample)
        
        title = f"{meta_data['coarse_label_names'][train_data['coarse_labels'][sample_id]]}:"
        title += f"{meta_data['fine_label_names'][train_data['fine_labels'][sample_id]]}"
        
        ax[i][j].set_title(title)

Статистика набора данных с использованием Plotly¶

In [8]:
train_data['data'] = [sample for sample in train_data['data']]
test_data['data'] = [sample for sample in test_data['data']]
In [9]:
train_df = pd.DataFrame(data=train_data, columns=list(train_data.keys()))
train_df.drop(labels=['batch_label'], axis=1)
Out[9]:
filenames fine_labels coarse_labels data
0 bos_taurus_s_000507.png 19 11 [255, 255, 255, 255, 255, 255, 255, 255, 255, ...
1 stegosaurus_s_000125.png 29 15 [255, 253, 253, 253, 253, 253, 253, 253, 253, ...
2 mcintosh_s_000643.png 0 4 [250, 248, 247, 248, 249, 249, 248, 248, 247, ...
3 altar_boy_s_001435.png 11 14 [124, 131, 135, 138, 140, 144, 148, 152, 156, ...
4 cichlid_s_000031.png 1 1 [43, 32, 87, 127, 55, 48, 51, 87, 121, 113, 13...
... ... ... ... ...
49995 tree_squirrel_s_000969.png 80 16 [130, 127, 131, 142, 127, 117, 138, 154, 156, ...
49996 tiger_beetle_s_000315.png 7 7 [167, 199, 193, 192, 195, 203, 188, 210, 200, ...
49997 bear_s_000631.png 3 8 [248, 240, 236, 234, 237, 236, 237, 238, 235, ...
49998 beetle_s_000411.png 7 7 [156, 151, 151, 151, 154, 154, 151, 151, 155, ...
49999 mako_s_001274.png 73 1 [31, 30, 31, 32, 31, 31, 32, 31, 30, 30, 31, 3...

50000 rows × 4 columns

In [10]:
fig = px.histogram(train_df, x="fine_labels", title='Баланc классов(меток) по набору данных')
fig.show()
In [11]:
fig = px.histogram(train_df, x="coarse_labels", title='Баланc классов(меток) по набору данных')
fig.show()

Как видно из гистограмм выше, все классы и суперклассы распределены равномерно. При этом к каждой метке класса сопоставлено по 500 изображений из трегировочного набора, а к каждому суперклассу - 2500 изображений.

In [12]:
def extract_channel(array, channel_id):
    return array.reshape(3, 32 * 32)[channel_id].reshape(32 * 32)
    
histograms = {
    "red": np.zeros((256), dtype=np.int32),
    "green": np.zeros((256), dtype=np.int32),
    "blue": np.zeros((256), dtype=np.int32)
}

for i, row in train_df.iterrows():
    data = row['data']
    histograms['red'] += np.histogram(extract_channel(data, 0), bins=256)[0]
    histograms['green'] += np.histogram(extract_channel(data, 1), bins=256)[0]
    histograms['blue'] += np.histogram(extract_channel(data, 2), bins=256)[0]


df = pd.DataFrame(data=histograms, index=[i for i in range(256)])
In [13]:
px.bar(df, title='Распределение пикселей по набору данных')

Применение модели из открытых источников к набору данных¶

GitHub: CIFAR-100 CNN¶

Используется простейшая сверточная нейронная сеть для решения задачи классификации на наборе данных CIFAR-100.

In [25]:
import datetime
import numpy as np
from sklearn import metrics

import tensorflow as tf
import keras
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense

Подготовка данных.¶

Изображения приводятся к формату np.ndarray((3, 32, 32)). К меткам применяется One-hot encoding.

In [15]:
train_dec, test_dec, meta_dec = load_from_pickle_CIFAR100("./cifar-100-python")

train_data = np.array([train_dec['data'][i].reshape(3, 32, 32).transpose(1, 2, 0) for i in range(len(train_dec['data']))])
test_data = np.array([test_dec['data'][i].reshape(3, 32, 32).transpose(1, 2, 0) for i in range(len(test_dec['data']))])
train_labels = np.array([[label] for label in train_dec['coarse_labels']], dtype=np.int64)
test_labels = np.array([[label] for label in test_dec['coarse_labels']], dtype=np.int64)

train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)

print('==================== Prepared data shapes =================')
print('Train data shape:', train_data.shape)
print('Test  data shape:', test_data.shape)
print('Train labels shape:', train_labels.shape)
print('Test  labels shape:', test_labels.shape)
==================== Prepared data shapes =================
Train data shape: (50000, 32, 32, 3)
Test  data shape: (10000, 32, 32, 3)
Train labels shape: (50000, 20)
Test  labels shape: (10000, 20)

Реализация пайплайна модели.¶

Модель имеет простейший препроцессинг: Нормализация данных. Формула: $X_i = \frac{X_i}{255}$ Выход модели представляет собой вектор из 20 float значений которые обладают свойством: $\sum_{i=1}^{20}x_i = 1$, что позволяет интерпретировать этот вектор как вектор оценок вероятностей принадлежности изображения к классу определенный моделью.

In [16]:
class ModelPipeline:
    """
    Базовый класс реализующий пайплайн модели классификации над набором данных CIFAR-100 [Суперклассы].
    """
    def __init__(self, weights_path: str=''):
        """
        Инициализация модели.
        Inputs:
            weights_path [str] - Путь к файлу с весами модели. Если он не указан, то инициализация пройдет с нулевыми весами.
                                 Default: ''
        """
        self.tfmodel = keras.Sequential()
        self.model_weights_path = weights_path
        
        self.__data_scale__ = 255.0
        self.__init_model__()
        
        
    def __init_model__(self):
        self.tfmodel.add(Conv2D(32, kernel_size = (3,3), activation='relu', padding = 'same', input_shape = (32,32,3))) 
        self.tfmodel.add(MaxPooling2D((2, 2)))
        self.tfmodel.add(Conv2D(64, kernel_size = (3,3), activation='relu', padding = 'same')) 
        self.tfmodel.add(MaxPooling2D((2, 2)))
        self.tfmodel.add(Conv2D(128, kernel_size = (3,3), activation='relu', padding = 'same'))
        self.tfmodel.add(MaxPooling2D((2, 2)))
          
        self.tfmodel.add(Flatten())
        self.tfmodel.add(Dense(128, activation='relu'))
        self.tfmodel.add(Dense(20, activation='softmax'))
        
        print("Inited model:")
        self.tfmodel.summary()
        optimizer = Adam(learning_rate=0.003)
        self.tfmodel.compile(optimizer=optimizer, loss='categorical_crossentropy', metrics=['accuracy'])
        
        if self.model_weights_path:
            self.tfmodel.load_weights(self.model_weights_path)
            print("Model weights loaded:", self.model_weights_path)
    
    def __preprocessing__(self, sample):
        return sample / self.__data_scale__
    
    def evaluate(self, data: np.ndarray, labels: np.ndarray):
        """
        Валидирование модели.
        Inputs:
            data   [np.ndarray] - Набор из изображений для валидации. Должен состоять из N изображений 32х32х3.
            labels [np.ndarray] - Соответсвующая набору data разметка данных по суперклассам.
        Return:
            Tuple[float, float] - Кортеж:
                                  0 [float] - Значение Loss функции для проведенного замера точности.
                                  1 [float] - Значение Accuracy для проведенного замера точности.
        """
        return self.tfmodel.evaluate(self.__preprocessing__(data), labels)
    
    def prediction(self, data: np.ndarray):
        """
        Inputs:
            data [np.ndarray] - Набор из изображений для предсказания меток. Должен состоять из N изображений 32x32x3.
        Return:
            np.ndarray - Массив вида np.ndarray(N, 20). По первой оси - индекс соответствующий изображению из data. 
                         По второй оси - 20 float элементов. i-й элемент показывает оценку вероятности принадлежности изображения к i-ой метке.
        """
        return self.tfmodel.predict(self.__preprocessing__(data))
In [17]:
MODEL_WEIGTHS_PATH = r'./model/CNN_100.hdf5'

pipeline = ModelPipeline(MODEL_WEIGTHS_PATH)
Inited model:
Model: "sequential"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 conv2d (Conv2D)             (None, 32, 32, 32)        896       
                                                                 
 max_pooling2d (MaxPooling2D  (None, 16, 16, 32)       0         
 )                                                               
                                                                 
 conv2d_1 (Conv2D)           (None, 16, 16, 64)        18496     
                                                                 
 max_pooling2d_1 (MaxPooling  (None, 8, 8, 64)         0         
 2D)                                                             
                                                                 
 conv2d_2 (Conv2D)           (None, 8, 8, 128)         73856     
                                                                 
 max_pooling2d_2 (MaxPooling  (None, 4, 4, 128)        0         
 2D)                                                             
                                                                 
 flatten (Flatten)           (None, 2048)              0         
                                                                 
 dense (Dense)               (None, 128)               262272    
                                                                 
 dense_1 (Dense)             (None, 20)                2580      
                                                                 
=================================================================
Total params: 358,100
Trainable params: 358,100
Non-trainable params: 0
_________________________________________________________________
Model weights loaded: ./model/CNN_100.hdf5
2022-12-17 01:52:25.416051: E tensorflow/compiler/xla/stream_executor/cuda/cuda_driver.cc:267] failed call to cuInit: CUDA_ERROR_SYSTEM_DRIVER_MISMATCH: system has unsupported display driver / cuda driver combination
2022-12-17 01:52:25.416092: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:169] retrieving CUDA diagnostic information for host: ilya-lenovo-pad
2022-12-17 01:52:25.416101: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:176] hostname: ilya-lenovo-pad
2022-12-17 01:52:25.416226: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:200] libcuda reported version is: 525.60.11
2022-12-17 01:52:25.416258: I tensorflow/compiler/xla/stream_executor/cuda/cuda_diagnostics.cc:204] kernel reported version is: NOT_FOUND: could not find kernel module information in driver version file contents: "NVRM version: NVIDIA UNIX Open Kernel Module for x86_64  520.56.06  Release Build  (dvs-builder@U16-T12-10-2)  Thu Oct  6 21:33:54 UTC 2022
GCC version:  gcc version 9.4.0 (Ubuntu 9.4.0-1ubuntu1~20.04.1) 
"
2022-12-17 01:52:25.416759: I tensorflow/core/platform/cpu_feature_guard.cc:193] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX2 FMA
To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.

Тестирование модели и визуализация¶

In [18]:
loss, accuracy = pipeline.evaluate(test_data, test_labels)
print('Test accuracy = '+str(accuracy)+' Test Loss = '+str(loss))
313/313 [==============================] - 2s 7ms/step - loss: 1.8203 - accuracy: 0.4437
Test accuracy = 0.44369998574256897 Test Loss = 1.8203390836715698
In [19]:
predicts = pipeline.prediction(test_data)
predictions_labels = [(meta_dec['coarse_label_names'][np.argmax(predicts[i])], np.max(predicts[i])) for i in range(len(predicts))]
313/313 [==============================] - 2s 6ms/step
In [20]:
nrows = 3
ncols = 3

fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(15, 16))

for i in range(nrows):
    for j in range(ncols):
        
        idx = i * ncols + j
        sample = test_data[idx]
        ax[i][j].imshow(sample)
        
        title = f"{predictions_labels[idx][0]}. Conf: {int(round(predictions_labels[idx][1], 2) * 100)}%"
        
        ax[i][j].set_title(title)

Использование Tensorboard¶

На простом примере продемонстирую как используется Tensorboard доска

In [21]:
train_dec, test_dec, meta_dec = load_from_pickle_CIFAR100("./cifar-100-python")

train_data = np.array([train_dec['data'][i].reshape(3, 32, 32).transpose(1, 2, 0) for i in range(len(train_dec['data']))])
test_data = np.array([test_dec['data'][i].reshape(3, 32, 32).transpose(1, 2, 0) for i in range(len(test_dec['data']))])
train_labels = np.array([[label] for label in train_dec['coarse_labels']], dtype=np.int64)
test_labels = np.array([[label] for label in test_dec['coarse_labels']], dtype=np.int64)

print('Train data shape:', train_data.shape)
print('Test  data shape:', test_data.shape)
print('Train labels shape:', train_labels.shape)
print('Test  labels shape:', test_labels.shape)
Train data shape: (50000, 32, 32, 3)
Test  data shape: (10000, 32, 32, 3)
Train labels shape: (50000, 1)
Test  labels shape: (10000, 1)
In [22]:
def create_model():
    return tf.keras.models.Sequential([
        tf.keras.layers.Conv2D(32, kernel_size=(3, 3), activation="relu", padding="same", input_shape=(32, 32, 3)),
        tf.keras.layers.MaxPooling2D(2, 2),
        tf.keras.layers.Conv2D(64, kernel_size=(3, 3), activation="relu", padding="same"),
        tf.keras.layers.Conv2D(64, kernel_size=(1, 1), activation="relu", padding="same"),
        tf.keras.layers.Conv2D(64, kernel_size=(3, 3), activation="relu", padding="same"),
        tf.keras.layers.MaxPooling2D(2, 2),
        tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation="relu", padding="same"),
        tf.keras.layers.Conv2D(128, kernel_size=(1, 1), activation="relu", padding="same"),
        tf.keras.layers.Conv2D(128, kernel_size=(3, 3), activation="relu", padding="same"),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dense(20, activation='softmax')
    ])
In [23]:
def train_model():
    model = create_model()
    model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
    logdir = os.path.join("logs", datetime.datetime.now().strftime("%Y%m%d-%H%M%S"))
    tensorboard_callback = tf.keras.callbacks.TensorBoard(logdir, histogram_freq=1)

    model.fit(x=x_train, 
            y=y_train, 
            epochs=3,
            validation_data=(x_test, y_test), 
            callbacks=[tensorboard_callback])
    return model
In [26]:
x_train, x_test = train_data / 255.0, test_data / 255.0
y_train, y_test = train_labels, test_labels
model = train_model()
Epoch 1/3
1563/1563 [==============================] - 103s 65ms/step - loss: 2.4923 - accuracy: 0.2248 - val_loss: 2.2773 - val_accuracy: 0.2929
Epoch 2/3
1563/1563 [==============================] - 104s 67ms/step - loss: 2.0952 - accuracy: 0.3497 - val_loss: 2.0446 - val_accuracy: 0.3686
Epoch 3/3
1563/1563 [==============================] - 105s 67ms/step - loss: 1.8867 - accuracy: 0.4107 - val_loss: 1.8778 - val_accuracy: 0.4166
In [27]:
%tensorboard --logdir ./logs/
ERROR: Could not find `tensorboard`. Please ensure that your PATH
contains an executable `tensorboard` program, or explicitly specify
the path to a TensorBoard binary by setting the `TENSORBOARD_BINARY`
environment variable.

Tensorboard output:¶

Демонстрация результата в Tensorboard

Применение сложного препроцессинга к набору данных¶

В качестве сложного препроцессинга был выбран препроцессинг повышения разрешения изображений. Используется модель из репозитория Real-ESRGAN.

Модель повышает разрешение изображения в 4 раза с помощью нейросетевых методов.

Скачивание весов модели

In [ ]:
!wget https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth -P weights

Реализация обращения к модели из Python кода¶

In [28]:
import cv2
import glob
import os
from basicsr.archs.rrdbnet_arch import RRDBNet
from basicsr.utils.download_util import load_file_from_url

from realesrgan import RealESRGANer
from realesrgan.archs.srvgg_arch import SRVGGNetCompact

class Enhancer:
    def __init__(self, weights_path='weights/RealESRGAN_x4plus.pth'):
        self.model_path = weights_path
        self.tile = 0
        self.tile_pad = 10
        self.pre_pad = 0
        self.half = False
        self.gpu_id = None
        self.outscale = 4
        
        
        self.__is_inited__ = False
        self.__upsampler__ = None
    
    def init(self):
        self.__is_inited__ = True
        
        model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
        netscale = 4
        file_url = ['https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth']
        
        # restorer
        self.__upsampler__ = RealESRGANer(
            scale=netscale,
            model_path=self.model_path,
            dni_weight=None,
            model=model,
            tile=self.tile,
            tile_pad=self.tile_pad,
            pre_pad=self.pre_pad,
            half=self.half,
            gpu_id=self.gpu_id)
        
    def infer(self, data: np.ndarray) -> np.ndarray:
        if not self.__is_inited__:
            raise RuntimeError("Please run Enhancer.init() before usage.")
            
        if len(data.shape) == 3:
            output, _ = self.__upsampler__.enhance(data, outscale=self.outscale)
            return output
        elif len(data.shape) == 4:
            
            outputs = list()
            for sample in data:
                output, _ = self.__upsampler__.enhance(sample, outscale=self.outscale)
                outputs.append(output)
                
            return np.array(outputs)
        
        return None
/home/ilya/anaconda3/envs/MAI-AI-CW/lib/python3.10/site-packages/tqdm/auto.py:22: TqdmWarning:

IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html

Инференс модели на нескольких изображениях из набора данных и визуализация результата¶

In [29]:
enhancer = Enhancer()
enhancer.init()
output = enhancer.infer(train_data[:10])
/home/ilya/anaconda3/envs/MAI-AI-CW/lib/python3.10/site-packages/torch/cuda/__init__.py:88: UserWarning:

CUDA initialization: Unexpected error from cudaGetDeviceCount(). Did you run some cuda functions before calling NumCudaDevices() that might have already set an error? Error 803: system has unsupported display driver / cuda driver combination (Triggered internally at ../c10/cuda/CUDAFunctions.cpp:109.)

In [50]:
import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=6, ncols=2, figsize=(5, 15))

for i in range(6):
    ax[i][0].tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)
    ax[i][1].tick_params(left=False, labelleft=False, bottom=False, labelbottom=False)
    ax[i][0].set_title('before')
    ax[i][1].set_title('after')
    ax[i][0].imshow(train_data[i])
    ax[i][1].imshow(output[i])